effective java中文版第三版 电子书 effective java百度云 您所在的位置:网站首页 effective java pdf百度云 effective java中文版第三版 电子书 effective java百度云

effective java中文版第三版 电子书 effective java百度云

2024-07-09 22:57| 来源: 网络整理| 查看: 265

Item 27 不要使用原始类型

1 // Raw collection type - don't do this! 2 // My stamp collection. Contains only Stamp instances. 3 private final Collection stamps = ... ;

1 // Erroneous insertion of coin into stamp collection 2 stamps.add(new Coin( ... )); // Emits "unchecked call" warning

// Raw iterator type - don't do this! for (Iterator i = stamps.iterator(); i.hasNext(); ) Stamp stamp = (Stamp) i.next(); // Throws ClassCastException stamp.cancel();

1、使用原始类型不会产生编译期错误,但会产生运行期错误,增加debug难度。

1 // Parameterized collection type - typesafe 2 private final Collection stamps = ... ;

2、虽然使用原始类型是合法的,但是不应该这样做,这会丧失类型安全以及泛型在表达方面的优势。

3、必须使传递含有参数类型的实例 给 被设计为原始类型的方法 合法,反之亦然。这就是所谓的移植性,为了兼容之前版本。

4、List 是原始类型List 的子类 但不是 List的子类。

5、使用原始类型会失去类型安全但是 List不会。

6、当不确定方法的具体参数类型时,也不要使用原始类型。

1 // Use of raw type for unknown element type - don't do this! 2 static int numElementsInCommon(Set s1, Set s2) { 3 int result = 0; 4 for (Object o1 : s1) 5 if (s2.contains(o1)) 6 result++; 7 return result; 8 }

而应该使用:

1 // Uses unbounded wildcard type - typesafe and flexible 2 static int numElementsInCommon(Set s1, Set s2) { ... }

7、在 class literals 中必须使用原始类型,例如List,class,String[].class。

8、在 instanceof 操作符中必须使用原始类型:

1 // Legitimate use of raw type - instanceof operator 2 if (o instanceof Set) { // Raw type 3 Set s = (Set) o; // Wildcard type 4 ... 5 }

注意第三行的类型转换是必须的。

9、是指能放入任何对象,是指能放入 任何一种 目前未知的对象

 

Item 28 比起数组优先使用List

1、数组是协变的,即a是b的子类 则a[]是b[]的子类;List则不是List的子类。

2、List在编译期就会指出类型不匹配:

1 // Fails at runtime! 2 Object[] objectArray = new Long[1]; 3 objectArray[0] = "I don't fit in"; // Throws ArrayStoreException

1 // Won't compile! 2 List ol = new ArrayList(); // Incompatible types 3 ol.add("I don't fit in");

3、arrays的类型是具体的;而泛型是通过编译期确定类型然后在运行期擦除。

4、创建一个泛型数组或是参数化类型的数组是非法的。例如,new List[], new List[], new E[]。

5、一般来说不可能在一个泛型集合中返回元素的数组。

6:

1 // Chooser - a class badly in need of generics! 2 public class Chooser { 3 private final Object[] choiceArray; 4 public Chooser(Collection choices) { 5 choiceArray = choices.toArray(); 6 } 7 public Object choose() { 8 Random rnd = ThreadLocalRandom.current(); 9 return choiceArray[rnd.nextInt(choiceArray.length)]; 10 } 11 }

在调用其中的choose()的方法后,必须手动转化类型,否则会导致运行错误。

1 // A first cut at making Chooser generic - won't compile 2 public class Chooser { 3 private final T[] choiceArray; 4 public Chooser(Collection choices) { 5 choiceArray = choices.toArray(); //choiceArray = (T[]) choices.toArray(); 6 } 7 // choose method unchanged 8 }

使用注释后的语句会变成一个warning,泛型的类型信息将会在被运行时擦除,因此编译器无法保证类型转化的正确。

 

1 // List-based Chooser - typesafe 2 public class Chooser { 3 private final List choiceList; 4 public Chooser(Collection choices) { 5 choiceList = new ArrayList(choices); 6 } 7 public T choose() { 8 Random rnd = ThreadLocalRandom.current(); 9   return choiceList.get(rnd.nextInt(choiceList.size())); 10 } 11 }

 

Item 29 支持泛型类型

1、泛型话一个类,首先是在类的声明中添加一个或多个参数类型,用于代表这个类中元素的类型。

2、然后将其中所有Object类型替换为 E的相关类型:

1 public class Stack { 2 private E[] elements; 3 private int size = 0; 4 private static final int DEFAULT_INITIAL_CAPACITY = 16; 5 6 public Stack() { 7 elements = new E[DEFAULT_INITIAL_CAPACITY]; 8 } 9 10 public void push(E e) { 11 ensureCapacity(); 12 elements[size++] = e; 13 } 14 15 public E pop() { 16 if (size == 0) 17 throw new EmptyStackException(); 18 E result = elements[--size]; 19 elements[size] = null; // Eliminate obsolete reference 20 return result; 21 } 22 ... // no changes in isEmpty or ensureCapacity 23 }

 

 

3、值得注意是由于你不能创建一个未定型的数组 因此 将Obiect[],替换为E[]会导致编译出错。

4、解决方法是 一是仍然保留在第6行的Object[],然后在其前面增加E[] 进行强制转型。但是会产生一个unchecked warning。二是在elements声明处,保留为Object[].但是在18行会出现错误,需要手动转型,同样会产生一个unchecked warning。

1 // Appropriate suppression of unchecked warning 2 public E pop() { 3 if (size == 0) 4 throw new EmptyStackException(); 5 // push requires elements to be of type E, so cast is correct 6 @SuppressWarnings("unchecked") E result =(E) elements[--size]; 7 elements[size] = null; // Eliminate obsolete reference 8 return result; 9 }

5、 注意使用Stack这种基本类型是不行的,需要使用装箱类型。

 

 

Item 30 支持泛型方法

1、例子:

1 // Uses raw types - unacceptable! (Item 26) 2 public static Set union(Set s1, Set s2) { 3 Set result = new HashSet(s1); 4 result.addAll(s2); 5 return result; 6 }

1 // Generic method 2 public static Set union(Set s1, Set s2) { 3 Set result = new HashSet(s1); 4 result.addAll(s2); 5 return result; 6 }

2、泛型单例工厂:

1 // Generic singleton factory pattern 2 private static UnaryOperator IDENTITY_FN = (t) -> t; 3 4 @SuppressWarnings("unchecked") 5 public static UnaryOperator identityFunction() { 6 return (UnaryOperator) IDENTITY_FN; 7 }

1 // Sample program to exercise generic singleton 2 public static void main(String[] args) { 3 String[] strings = { "jute", "hemp", "nylon" }; 4 UnaryOperator sameString = identityFunction(); 5 for (String s : strings) 6 System.out.println(sameString.apply(s)); 7 8 Number[] numbers = { 1, 2.0, 3L }; 9 UnaryOperator sameNumber = identityFunction(); 10 for (Number n : numbers) 11 System.out.println(sameNumber.apply(n)); 12 }

3、T可以和Comparable进行比较。

4:互相比较:

1 // Using a recursive type bound to express mutual comparability 2 public static E max(Collection c);

1 // Returns max value in a collection - uses recursive type bound 2 public static E max(Collection c) { 3 if (c.isEmpty()) 4 throw new IllegalArgumentException("Empty collection"); 5 E result = null; 6 for (E e : c) 7 if (result == null || e.compareTo(result) > 0) 8     result = Objects.requireNonNull(e); 9 return result; 10 }

 

Item 31  使用绑定通配符增加API的灵活性

1、参数类型是不变量,也就是说不管Type1和Type2是什么关系,List与List不会有继承关系。

2、例子 :

1 public class Stack { 2 public Stack(); 3 public void push(E e); 4 public E pop(); 5 public boolean isEmpty(); 6 }

如果想增加一个addAll方法:

1 // pushAll method without wildcard type - deficient! 2 public void pushAll(Iterable src) { 3 for (E e : src) 4 push(e); 5 }

如果要进行如下操作:

1 Stack numberStack = new Stack(); 2 Iterable integers = ... ; 3 numberStack.pushAll(integers);

会出现在错误,因为虽然Integer是Number的子类,但是作为参数类型是不变量。

因此应该使用如下绑定通配符:

1 // Wildcard type for a parameter that serves as an E producer 2 public void pushAll(Iterable list, int i, int j);

然后他的一种直接的实现无法通过编译:

1 public static void swap(List list, int i, int j) { 2 list.set(i, list.set(j, list.get(i))); 3 }

错误的原因是 不能将任何值除了null放进List中!

解决方法如下:实质就是使用第一种声明方式:

1 public static void swap(List list, int i, int j) { 2 swapHelper(list, i, j); 3 } 4 // Private helper method for wildcard capture 5 private static void swapHelper(List list, int i, int j) { 6 list.set(i, list.set(j, list.get(i))); 7 }

这样多此一举的好处是,使外部的使用者简单明了的直接使用,而不需要看见具体繁琐的声明。

 

Item 32 谨慎的结合泛型和可变参数(参数个数可变)

1、可变参数方法是 抽象泄漏 (leaky abstraction)的。因为当你使用可变参数方法时,会创建一个数组用于储存可变参数,而这个数组作为实现细节原本应该是不应该可见的,可是在这里可见了。

2、非具体类型是指在运行时的信息比编译期时的信息少的类型,几乎所有泛型和参数类型都是非具体类型。

3、如果把非具体类型作为可变参数 ,在它的声明处会出现一个编译器警告。会提示产生 堆污染 (heap pollution):

1 // Mixing generics and varargs can violate type safety! 2 static void dangerous(List... stringLists) { 3 List intList = List.of(42); 4 Object[] objects = stringLists; 5 objects[0] = intList; // Heap pollution 6 String s = stringLists[0].get(0); // ClassCastException 7 }

4、将一个值储存在泛型可变参数数组是不安全的。

5、在Java 7中 添加了 @SafeVarargs 注解用来 抑制由于使用了 泛型可变参数 而产生的警告。只有在确认这个方法真的安全的情况下才去使用!

6、如果在作为 方法参数 的 泛型可变参数 的 数组中 该方法不改变这个数组 并同时 不让其他不可信代码 获得这个数组的引用,那他就是安全的。也就是 参数仅仅在调用者和方法间进行传递!

7、例子:

1 // UNSAFE - Exposes a reference to its generic parameter array! 2 static T[] toArray(T... args) { 3 return args; 4 }

1 static T[] pickTwo(T a, T b, T c) { 2 switch(ThreadLocalRandom.current().nextInt(3)) { 3 case 0: return toArray(a, b); 4 case 1: return toArray(a, c); 5 case 2: return toArray(b, c); 6 } 7 throw new AssertionError(); // Can't get here 8 }

编译的时候,编译器创建了一个泛型可变数组将两个T实例传递给 toArray。 而这个数组的类型是Object[].

 

1 public static void main(String[] args) { 2 String[] attributes = pickTwo("Good", "Fast", "Cheap"); 3 }

 

会产生类型转化错误 因为无法将Object[]转化为 String[]。也就是之前所说的 不让其他不可信代码 获得这个数组的引用

8、正确的使用方法:

1 // Safe method with a generic varargs parameter 2 @SafeVarargs 3 static List flatten(List


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有